/*
 * Copyright (c) 2023 orchy <orchy12@proton.me>
 * This file is licensed under the MIT/Expat license.
 * You should have received a copy of the license with this software, if not see
 * https://spdx.org/licenses/MIT.html
 */
#ifndef _i_will_not_get_support
#	if !(defined(__linux) || defined(__linux__))
#		error Unsupported OS, define '_i_will_not_get_support' to ignore
#	endif
#	if !(defined(__x86_64) || defined(__x86_64__))
#		error Unsupported CPU, define '_i_will_not_get_support' to ignore
#	endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <asm/unistd_64.h> /* for __NR_ioctl */
#include <asm-generic/ioctls.h> /* for TIOCGWINSZ */
#include <sys/ioctl.h> /* for winsize */
#include <assert.h>

void usage(FILE* stream);

int main(int argc, char** argv, char** envp) {
	/* i have no idea about a lot of this, it's cobbled together from varius
	 * search results about creating a shell and ptrace
	 */
	if (argc < 4) {
		usage(stderr);
		return EXIT_FAILURE;
	}
	pid_t pid = fork();
	if (pid == 0) {
		ptrace(PTRACE_TRACEME, 0, NULL, NULL);
		if (execve(argv[3], argv+3, envp) == -1) {
			perror("execve");
			exit(EXIT_FAILURE);
		}
	} else if (pid < 0) {
		perror("fork");
	} else {
		int status;
		do {
			ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
			waitpid(pid, &status, 0);
			struct user_regs_struct regs;
			ptrace(PTRACE_GETREGS, pid, NULL, &regs);
			long syscall = regs.orig_rax;
			if (syscall == __NR_ioctl && regs.rsi == TIOCGWINSZ) {
				/* its getting window size! */
				/* TODO less hacky and less ub */
				struct winsize ws = {atoi(argv[1]), atoi(argv[2]),
						     0, 0};
				assert(sizeof(struct winsize) == sizeof(long));
				ptrace(PTRACE_POKEDATA, pid, regs.rdx, *(long*)&ws);
			}
			ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
			waitpid(pid, &status, 0);
		} while (!WIFEXITED(status) && !WIFSIGNALED(status));
	}
}

void usage(FILE* stream) {
	fprintf(stream, "usage: launch rows cols program [arguments]\n"
			"\n"
			"Launches specified program (no $PATH resolution) with\n"
			"specified arguments and lies about the terminal size.\n");
}
